#include "CollisionManager.h"

#ifdef SHOULD_ALLOC_AABB_RENDERING_DATA
//Include effect and shader manager
#include "ShaderManager.h"
#include "TileForwardSolidColourFX.h"

//Will need graphics interface for device (create) and context (rendering). 
#include "GraphicsInterface.h"

//And the camera for certain vars used when rendering
#include "VirtualCamera.h"

//Need geo gen to generate box - we could do it all ourselves, but this is easier. 
#include "GeometryGenerator.h"

//Render states for alpha blending
#include "RenderStates.h"
#endif

//init static data
CollisionManager* CollisionManager::instance = 0;

CollisionManager::CollisionManager()
{
#ifdef SHOULD_ALLOC_AABB_RENDERING_DATA
	//Init pointers to NULL. 
	boxVB = 0;
	boxIB = 0;

	//Create the box
	LoadBoxVertexData();
#endif
}

CollisionManager::~CollisionManager()
{
#ifdef SHOULD_ALLOC_AABB_RENDERING_DATA
	//Destroy IB and VB of box. 
	ReleaseCOM(boxVB);
	ReleaseCOM(boxIB);
#endif
}

CollisionManager* CollisionManager::GetInstance()
{
	if (!instance)
		instance = new CollisionManager();

	return instance;
}

void CollisionManager::Release()
{
	if (instance)
		SafeDelete(instance);
}

float CollisionManager::CalculateUnsignedDistance(XMFLOAT3 a, XMFLOAT3 b)
{
	//Load float3's in to vectors so we can get the distance. 
	XMVECTOR aVec = XMLoadFloat3(&a);
	XMVECTOR bVec = XMLoadFloat3(&b);
	
	//Vector from a to b
	XMVECTOR toVec = bVec - aVec;

	//Get Lenght
	XMVECTOR len =  XMVector3Length(toVec);
	
	//Store in to a float
	float ret = 0.0f;
	XMStoreFloat(&ret, len);

	//return 
	return ret;
}

bool CollisionManager::TestCollision(AABB* a, AABB* b)
{
	//Loop through the two boxes and calculate the minimum and maximum
	//values (in world space) of each axis. 
	XMFLOAT3 aMin;
	XMFLOAT3 aMax;

	XMFLOAT3 bMin;
	XMFLOAT3 bMax;

	//calculate the maxium and minium vertices of bounding box a
	aMin.x = a->CenterW.x - (a->ExtentsW.x/2.0f);
	aMin.y = a->CenterW.y - (a->ExtentsW.y/2.0f);
	aMin.z = a->CenterW.z - (a->ExtentsW.z/2.0f);

	aMax.x = a->CenterW.x + (a->ExtentsW.x/2.0f);
	aMax.y = a->CenterW.y + (a->ExtentsW.y/2.0f);
	aMax.z = a->CenterW.z + (a->ExtentsW.z/2.0f);

	//calculate the maxium and minium vertices of bounding box b
	bMin.x = b->CenterW.x - (b->ExtentsW.x/2.0f);
	bMin.y = b->CenterW.y - (b->ExtentsW.y/2.0f);
	bMin.z = a->CenterW.z - (b->ExtentsW.z/2.0f);

	bMax.x = b->CenterW.x + (b->ExtentsW.x/2.0f);
	bMax.y = b->CenterW.y + (b->ExtentsW.y/2.0f);
	bMax.z = b->CenterW.z + (b->ExtentsW.z/2.0f);

	//checks maximum and minium of each bounding box agasint each other 
	//if one is within the other then return true else return false
	return(aMax.x > bMin.x && 
    aMin.x < bMax.x &&
    aMax.y > bMin.y &&
    aMin.y < bMax.y &&
    aMax.z > bMin.z &&
    aMin.z < bMax.z);

}

#ifdef SHOULD_ALLOC_AABB_RENDERING_DATA
void CollisionManager::LoadBoxVertexData()
{
	//Instance will create geometry. 
	GeometryGenerator geoGen;

	//Holds VB and IB data...
	GeometryGenerator::MeshData data;

	//Create a box.
	geoGen.CreateBox(1.0f, 1.0f, 1.0f, data);

	//Extract Vertex (buffer) data
	//
	//Extract just position and  normal
	std::vector<TileForwardSolidColourFX::PosNormalVertex>verts(data.Vertices.size());

	for (unsigned i = 0; i < data.Vertices.size(); i++)
	{
		verts[i].PosL = data.Vertices[i].Position;
		verts[i].NormalL = data.Vertices[i].Normal;
	}

	//Create VB - Fill out the VB description
	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = sizeof(TileForwardSolidColourFX::PosNormalVertex) * data.Vertices.size();
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	//Create VB - Fill subresource struct
	D3D11_SUBRESOURCE_DATA sd;
	sd.pSysMem = &verts[0];

	//Create VB - In GPU memory. 
	GraphicsInterface::GetInstance()->GetDevice()->CreateBuffer(&bd, &sd, &boxVB);

	//Create IB - Modify above VB desc struct
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER; //Is an INDEX buffer.
	bd.ByteWidth = sizeof(UINT) * data.Indices.size();
	
	//Create IB - Modify subres struct.
	sd.pSysMem = &data.Indices[0];

	//Create IB in GPU memory
	GraphicsInterface::GetInstance()->GetDevice()->CreateBuffer(&bd, &sd, &boxIB);

	//Store number of indicies
	boxIndexCount = data.Indices.size();
}

void CollisionManager::DebugDrawAABB(AABB* box, VirtualCamera* camera, XMFLOAT3 colour)
{
	//Check inputs (if null, crash!)
	assert(box);
	assert(camera);

	//Construct matricies needed for this render. 
	XMMATRIX world = XMMatrixScaling(box->ExtentsW.x, box->ExtentsW.y, box->ExtentsW.z) * 
		XMMatrixTranslation(box->CenterW.x, box->CenterW.y, box->CenterW.z);
	XMMATRIX worldInvTrans = MathHelper::InverseTranspose(world);
	XMMATRIX worldViewProj = (world * camera->GetView()) * camera->GetProjection();
	XMMATRIX worldView = world * camera->GetView();

	//Slightly transparent
	Material material;
	material.Diffuse.w = 0.6f;

	//Get debug draw effect - one of the standard
	debugFX = ShaderManager::GetInstance()->ReturnTileForwardSolidColourFX();

	//Get device context - we ma ually set state and the like. 
	ID3D11DeviceContext* context = GraphicsInterface::GetInstance()->GetContext();

	//Store previous OM State.
	ID3D11BlendState* prevBlendState;
	float prevBlendFac[4];
	UINT prevSampleMask;
	context->OMGetBlendState(&prevBlendState, &prevBlendFac[0], &prevSampleMask);

	//Store prev RS state
	ID3D11RasterizerState* prevRSState;
	context->RSGetState(&prevRSState);

	//Enable alpha blendeing.
	context->OMSetBlendState(RenderStates::transparentBS, NULL, 0xffffffff);

	//Set active technique (colour render)
	debugFX->SetActiveTechniqueByName("Colour_NoSSAOSample");

	//Draw a box. Note that this is through a traditional forward
	//system and not our graphics interface. 
	//
	//Set IA Stage
	context->IASetInputLayout(debugFX->ReturnInputLayout());
	context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); //Triangle list.

	//Bind buffers. 
	UINT stride = sizeof(TileForwardSolidColourFX::PosNormalVertex);
	UINT offset = 0;
	context->IASetVertexBuffers(0, 1, &boxVB, &stride, &offset);
	context->IASetIndexBuffer(boxIB, DXGI_FORMAT_R32_UINT, 0);

	//Set effect vars - just what we need. 
	debugFX->SetCBufferPerObject(world, worldInvTrans, worldViewProj, material, colour, worldView);
	debugFX->SetLightingData(camera->GetPosition(), 0, 0, 0, 0);
	debugFX->SetEnvironmentMap(0);
	debugFX->SetShadowMap(0);
	debugFX->SetShadowMapMatricies(XMMatrixIdentity(), XMMatrixIdentity(), XMMatrixIdentity());
	debugFX->SetSSAOData(0, XMMatrixIdentity());

	//Draw box
	for (unsigned i = 0; i < debugFX->ReturnActiveTechniqueNumberOfPasses(); i++)
	{
		//Set pipeline states.
		debugFX->ApplyActiveTechniqueForPass(i, context);
		
		//Override and disable backface culling
		context->RSSetState(RenderStates::noCullRS);

		//Draw indexed. 
		context->DrawIndexed(boxIndexCount, 0, 0);
	}

	//Restore old state (OM)
	context->OMSetBlendState(prevBlendState, prevBlendFac, prevSampleMask);

	//Restore old RS state
	context->RSSetState(prevRSState);
}
#endif